Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit c3ffd7c38af749050898773a2c40c889c49e37e3


Parents : 2eef1cd
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-17T22:26:34-05:00

feat: dependency management with git dependency checks and format token validation

Changes

8 files changed, 524 insertions(+), 4 deletions(-)

M Taskfile.yml +72 -1
M package.json +1 -1

Diff

diff --git a/Taskfile.yml b/Taskfile.yml
index cf0622ef..6bad3120 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -103,6 +103,51 @@ tasks:
- echo "Warning - Installing packages over RNS via pip-rns is slow. Use for mesh-only environments."
- bash scripts/pip-rns-deps.sh {{.CLI_ARGS}}
+ deps:git-pins:
+ aliases: [deps:pins]
+ desc: Fail if github/git deps or WASM download URLs drift from scripts/deps-allowlist.json
+ cmds:
+ - node scripts/check-git-deps.mjs
+
+ deps:why:
+ desc: Explain why a package is installed (pnpm why and uv tree --package)
+ cmds:
+ - |
+ PKG="{{.CLI_ARGS}}"
+ if [ -z "$PKG" ]; then
+ echo "Usage: task deps:why -- <package>" >&2
+ exit 1
+ fi
+ echo "=== pnpm why ${PKG} ==="
+ {{.NPM}} why "$PKG" || true
+ echo
+ echo "=== uv tree --package ${PKG} --invert ==="
+ uv tree --package "$PKG" --invert || true
+
+ check:format-tokens:
+ desc: Fail if formatDate/meshDate patterns use unsupported datetime tokens
+ cmds:
+ - node scripts/check-format-tokens.mjs
+
+ licenses:refresh:
+ desc: Regenerate embedded license artifacts and fail if the tree is dirty afterward
+ cmds:
+ - uv run python -m meshchatx.src.backend.licenses_collector --write-artifacts
+ - |
+ if ! git diff --quiet -- \
+ meshchatx/src/backend/data/licenses_frontend.json \
+ meshchatx/src/backend/data/licenses_backend.json \
+ meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt
+ then
+ echo "licenses:refresh left a dirty tree. Commit the updated license artifacts." >&2
+ git status --short -- \
+ meshchatx/src/backend/data/licenses_frontend.json \
+ meshchatx/src/backend/data/licenses_backend.json \
+ meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt >&2
+ exit 1
+ fi
+ echo "licenses:refresh: artifacts up to date"
+
docs:rns:
aliases: [build-docs:rns]
desc: Fetch Reticulum manual from rngit website remote over RNS
@@ -180,11 +225,13 @@ tasks:
lint:frontend:
aliases: [lint:fe]
- desc: Lint frontend code (ESLint, vue-tsc, knip)
+ desc: Lint frontend code (ESLint, vue-tsc, knip, format-token and git-dep pin checks)
cmds:
- "{{.NPM}} run lint"
- "{{.NPM}} run typecheck"
- "{{.NPM}} exec knip"
+ - task: check:format-tokens
+ - task: deps:git-pins
lint:backend:
aliases: [lint:be]
@@ -283,6 +330,30 @@ tasks:
cmds:
- node scripts/mutation/run.mjs --source meshchatx/src/frontend/js/rnode/Capabilities.js --max-per-file 20
+ test:mutation:frontend:libs:
+ desc: MeshMut on meshchatx/src/frontend/libs (emitter uuid datetime clickOutside)
+ cmds:
+ - >-
+ node scripts/mutation/run.mjs
+ --source meshchatx/src/frontend/libs/emitter.js
+ --source meshchatx/src/frontend/libs/uuid.js
+ --source meshchatx/src/frontend/libs/datetime.js
+ --source meshchatx/src/frontend/libs/clickOutside.js
+ {{.CLI_ARGS}}
+
+ test:libs:
+ aliases: [test:fe:libs]
+ desc: Run in-repo frontend libs tests with coverage gate
+ cmds:
+ - >-
+ {{.NPM}} exec vitest run
+ tests/frontend/libs.core.test.js
+ tests/frontend/libs.oracle.test.js
+ tests/frontend/libs.index.test.js
+ --coverage
+ --coverage.include=meshchatx/src/frontend/libs/**
+ - node scripts/check-format-tokens.mjs
+
test:frontend:
aliases: [test:fe]
desc: Run frontend + Electron shell unit tests (vitest, excludes i18n, see test:lang, excludes LoadTimePerformance, use test:fe:loadtime locally)

diff --git a/meshchatx.rsm b/meshchatx.rsm
index 238da675..b5cf4a0d 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ

diff --git a/package.json b/package.json
index 51d122e7..81b164ff 100644
--- a/package.json
+++ b/package.json
@@ -90,7 +90,7 @@
"globals": "^17.6.0",
"jsdom": "^29.1.1",
"knip": "^6.24.0",
- "micron-parser": "github:RFnexus/micron-parser-js",
+ "micron-parser": "github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098",
"prettier": "^3.9.3",
"tailwindcss": "^4.3.0",
"terser": "^5.48.0",

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2ff9c10..94dea4cb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -193,7 +193,7 @@ importers:
specifier: ^6.24.0
version: 6.24.0
micron-parser:
- specifier: github:RFnexus/micron-parser-js
+ specifier: github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098
version: https://codeload.github.com/RFnexus/micron-parser-js/tar.gz/33feb1054c8b2cb3f5f05abbb8903360d3f0c098
prettier:
specifier: ^3.9.3
@@ -4270,7 +4270,7 @@ snapshots:
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
- string-width-cjs: string-width@4.2.0
+ string-width-cjs: string-width@4.2.3
strip-ansi: 6.0.1
strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0

diff --git a/scripts/check-format-tokens.mjs b/scripts/check-format-tokens.mjs
new file mode 100644
index 00000000..f8b205e3
--- /dev/null
+++ b/scripts/check-format-tokens.mjs
@@ -0,0 +1,125 @@
+#!/usr/bin/env node
+// SPDX-License-Identifier: 0BSD
+/**
+ * Fail when formatDate / meshDate(...).format patterns use unsupported tokens.
+ * Keeps meshchatx/src/frontend/libs/datetime.js as the single source of truth.
+ */
+import fs from "node:fs";
+import path from "node:path";
+import process from "node:process";
+import { fileURLToPath } from "node:url";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, "..");
+const FRONTEND_ROOT = path.join(ROOT, "meshchatx", "src", "frontend");
+const DATETIME_LIB = path.join(FRONTEND_ROOT, "libs", "datetime.js");
+
+const SCAN_EXTENSIONS = new Set([".js", ".mjs", ".cjs", ".vue", ".ts", ".tsx"]);
+
+/** @type {RegExp[]} */
+const PATTERN_EXTRACTORS = [
+ /\bformatDate\s*\(\s*[^,]+?\s*,\s*(['"`])([^'"`]+?)\1/g,
+ /\bmeshDate\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/g,
+ /\bdayjs\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/g,
+];
+
+/**
+ * @param {string} source
+ * @returns {string[]}
+ */
+function readSupportedTokens(source) {
+ const match = source.match(/export const SUPPORTED_FORMAT_TOKENS = Object\.freeze\(\[([\s\S]*?)\]\)/);
+ if (!match) {
+ return [];
+ }
+ return Array.from(match[1].matchAll(/"([^"]+)"/g), (m) => m[1]);
+}
+
+/**
+ * @param {string} dir
+ * @param {(filePath: string) => void} visit
+ */
+function walk(dir, visit) {
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ if (entry.name === "node_modules" || entry.name === "public" || entry.name.startsWith(".")) {
+ continue;
+ }
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ walk(full, visit);
+ continue;
+ }
+ if (SCAN_EXTENSIONS.has(path.extname(entry.name))) {
+ visit(full);
+ }
+ }
+}
+
+/**
+ * @param {string} pattern
+ * @param {ReadonlySet<string>} supported
+ * @returns {string[]}
+ */
+function unsupportedTokens(pattern, supported) {
+ const tokenRe = /YYYY|MMM|MM|M|DD|D|HH|H|hh|h|mm|A|a|ss|SSS|Z|z|X|x|Do|ddd|dddd/g;
+ const found = pattern.match(tokenRe) || [];
+ return [...new Set(found.filter((token) => !supported.has(token)))];
+}
+
+function main() {
+ const datetimeSource = fs.readFileSync(DATETIME_LIB, "utf8");
+ const supportedList = readSupportedTokens(datetimeSource);
+ if (supportedList.length === 0) {
+ console.error("check-format-tokens: SUPPORTED_FORMAT_TOKENS missing from datetime.js");
+ process.exit(1);
+ }
+ const supported = new Set(supportedList);
+
+ /** @type {{ file: string, pattern: string, tokens: string[] }[]} */
+ const failures = [];
+ /** @type {{ file: string, pattern: string }[]} */
+ const dayjsHits = [];
+
+ walk(FRONTEND_ROOT, (filePath) => {
+ const rel = path.relative(ROOT, filePath).replace(/\\/g, "/");
+ if (rel.endsWith("libs/datetime.js")) {
+ return;
+ }
+ const text = fs.readFileSync(filePath, "utf8");
+ for (const extractor of PATTERN_EXTRACTORS) {
+ extractor.lastIndex = 0;
+ let match;
+ while ((match = extractor.exec(text)) !== null) {
+ const pattern = match[2];
+ if (extractor.source.includes("dayjs")) {
+ dayjsHits.push({ file: rel, pattern });
+ }
+ const bad = unsupportedTokens(pattern, supported);
+ if (bad.length > 0) {
+ failures.push({ file: rel, pattern, tokens: bad });
+ }
+ }
+ }
+ });
+
+ if (dayjsHits.length > 0) {
+ console.error("check-format-tokens: dayjs(...).format still used in frontend sources:");
+ for (const hit of dayjsHits) {
+ console.error(` ${hit.file}: ${hit.pattern}`);
+ }
+ process.exit(1);
+ }
+
+ if (failures.length > 0) {
+ console.error("check-format-tokens: unsupported format tokens:");
+ for (const failure of failures) {
+ console.error(` ${failure.file}: "${failure.pattern}" -> ${failure.tokens.join(", ")}`);
+ }
+ console.error(`Supported tokens: ${supportedList.join(" ")}`);
+ process.exit(1);
+ }
+
+ console.log("check-format-tokens: ok");
+}
+
+main();

diff --git a/scripts/check-git-deps.mjs b/scripts/check-git-deps.mjs
new file mode 100644
index 00000000..4f9abdfd
--- /dev/null
+++ b/scripts/check-git-deps.mjs
@@ -0,0 +1,267 @@
+#!/usr/bin/env node
+// SPDX-License-Identifier: 0BSD
+/**
+ * Fail when git-hosted deps or remote download URLs drift from scripts/deps-allowlist.json.
+ * Covers package.json github:/git+ specs, pnpm-lock pins, flatpak git sources, and Micron WASM URLs.
+ */
+import fs from "node:fs";
+import path from "node:path";
+import process from "node:process";
+import { fileURLToPath } from "node:url";
+import { MICRON_PARSER_GO_RELEASE_TAG } from "./micron-parser-go-version.mjs";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, "..");
+const ALLOWLIST_PATH = path.join(ROOT, "scripts", "deps-allowlist.json");
+const PACKAGE_JSON_PATH = path.join(ROOT, "package.json");
+const LOCKFILE_PATH = path.join(ROOT, "pnpm-lock.yaml");
+const FETCH_MICRON_WASM_PATH = path.join(ROOT, "scripts", "fetch-micron-wasm.mjs");
+
+const GIT_SPEC_RE = /^(github:|git\+|git:)/i;
+const FULL_COMMIT_RE = /^[0-9a-f]{40}$/i;
+const CODELOAD_COMMIT_RE = /codeload\.github\.com\/[^/]+\/[^/]+\/tar\.gz\/([0-9a-f]{40})/i;
+
+/**
+ * @param {string} pattern
+ * @param {string} value
+ */
+function matchPattern(pattern, value) {
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
+ return new RegExp(`^${escaped}$`).test(value);
+}
+
+/**
+ * @param {string} spec
+ */
+function splitGitSpec(spec) {
+ const hashIndex = spec.indexOf("#");
+ if (hashIndex < 0) {
+ return { base: spec, ref: null };
+ }
+ return {
+ base: spec.slice(0, hashIndex),
+ ref: spec.slice(hashIndex + 1),
+ };
+}
+
+/**
+ * @param {string} lockText
+ * @param {string} name
+ */
+function lockfileCommitForPackage(lockText, name) {
+ const lines = lockText.split(/\r?\n/);
+ let inPackage = false;
+ for (const line of lines) {
+ if (line.match(new RegExp(`^\\s{4}${name}:\\s*$`))) {
+ inPackage = true;
+ continue;
+ }
+ if (inPackage) {
+ if (/^\s{4}\S/.test(line) && !line.trim().startsWith(`${name}:`)) {
+ break;
+ }
+ const versionMatch = line.match(/^\s+version:\s+(\S+)\s*$/);
+ if (versionMatch) {
+ const codeload = versionMatch[1].match(CODELOAD_COMMIT_RE);
+ if (codeload) {
+ return codeload[1].toLowerCase();
+ }
+ }
+ const resolutionMatch = line.match(/^\s+resolution:\s+\{[^}]*tarball:\s+(\S+?)[,}]/);
+ if (resolutionMatch) {
+ const codeload = resolutionMatch[1].match(CODELOAD_COMMIT_RE);
+ if (codeload) {
+ return codeload[1].toLowerCase();
+ }
+ }
+ }
+ }
+
+ const globalCodeload = lockText.match(
+ new RegExp(`${name}@https://codeload\\.github\\.com/[^\\s]+/tar\\.gz/([0-9a-f]{40})`, "i")
+ );
+ if (globalCodeload) {
+ return globalCodeload[1].toLowerCase();
+ }
+ return null;
+}
+
+/**
+ * @param {string} text
+ * @param {string} constName
+ */
+function readExportedStringConst(text, constName) {
+ const re = new RegExp(`const\\s+${constName}\\s*=\\s*[\`"']([^\`"']+)[\`"']`);
+ const match = text.match(re);
+ return match ? match[1] : null;
+}
+
+function main() {
+ /** @type {string[]} */
+ const errors = [];
+
+ if (!fs.existsSync(ALLOWLIST_PATH)) {
+ console.error(`check-git-deps: missing allowlist ${path.relative(ROOT, ALLOWLIST_PATH)}`);
+ process.exit(1);
+ }
+
+ const allowlist = JSON.parse(fs.readFileSync(ALLOWLIST_PATH, "utf8"));
+ const packageJson = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, "utf8"));
+ const lockText = fs.readFileSync(LOCKFILE_PATH, "utf8");
+ const fetchMicronText = fs.readFileSync(FETCH_MICRON_WASM_PATH, "utf8");
+
+ const depSections = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
+ /** @type {{ name: string, spec: string }[]} */
+ const gitDeps = [];
+ for (const section of depSections) {
+ const block = packageJson[section];
+ if (!block || typeof block !== "object") continue;
+ for (const [name, spec] of Object.entries(block)) {
+ if (typeof spec === "string" && GIT_SPEC_RE.test(spec)) {
+ gitDeps.push({ name, spec });
+ }
+ }
+ }
+
+ const allowByName = new Map(
+ (allowlist.git_dependencies || []).map((entry) => [entry.name, entry])
+ );
+
+ for (const dep of gitDeps) {
+ const allowed = allowByName.get(dep.name);
+ if (!allowed) {
+ errors.push(`git dependency "${dep.name}" (${dep.spec}) is not in deps-allowlist.json`);
+ continue;
+ }
+
+ const { base, ref } = splitGitSpec(dep.spec);
+ const allowedBases = new Set(
+ (allowed.specifiers || []).map((spec) => splitGitSpec(spec).base)
+ );
+ if (!allowedBases.has(base) && !(allowed.specifiers || []).includes(dep.spec)) {
+ errors.push(
+ `git dependency "${dep.name}" specifier base "${base}" is not allowlisted (have: ${(allowed.specifiers || []).join(", ")})`
+ );
+ }
+
+ const lockCommit = lockfileCommitForPackage(lockText, dep.name);
+ if (!lockCommit) {
+ errors.push(`git dependency "${dep.name}" is unpinned in pnpm-lock.yaml (no 40-char commit)`);
+ continue;
+ }
+ if (!FULL_COMMIT_RE.test(lockCommit)) {
+ errors.push(`git dependency "${dep.name}" lock commit is not a full SHA: ${lockCommit}`);
+ continue;
+ }
+ if (lockCommit !== String(allowed.commit).toLowerCase()) {
+ errors.push(
+ `git dependency "${dep.name}" lock commit ${lockCommit} drifts from allowlist ${allowed.commit}`
+ );
+ }
+ if (ref && FULL_COMMIT_RE.test(ref) && ref.toLowerCase() !== String(allowed.commit).toLowerCase()) {
+ errors.push(
+ `git dependency "${dep.name}" package.json ref #${ref} drifts from allowlist ${allowed.commit}`
+ );
+ }
+ if (!ref) {
+ errors.push(
+ `git dependency "${dep.name}" package.json specifier is floating (${dep.spec}). Pin with #${allowed.commit}`
+ );
+ }
+ }
+
+ for (const allowed of allowlist.git_dependencies || []) {
+ if (!gitDeps.some((dep) => dep.name === allowed.name)) {
+ errors.push(
+ `allowlist git dependency "${allowed.name}" is missing from package.json (remove from allowlist or restore dep)`
+ );
+ }
+ }
+
+ const flatpakModules = packageJson.build?.flatpak?.modules || [];
+ const allowGitSources = allowlist.git_sources || [];
+ /** @type {object[]} */
+ const declaredGitSources = [];
+ for (const mod of flatpakModules) {
+ for (const source of mod.sources || []) {
+ if (source && source.type === "git" && source.url) {
+ declaredGitSources.push(source);
+ }
+ }
+ }
+
+ for (const source of declaredGitSources) {
+ const allowed = allowGitSources.find((entry) => entry.url === source.url);
+ if (!allowed) {
+ errors.push(`flatpak git source ${source.url} is not in deps-allowlist.json`);
+ continue;
+ }
+ if (allowed.commit && source.commit && source.commit !== allowed.commit) {
+ errors.push(
+ `flatpak git source ${source.url} commit ${source.commit} drifts from allowlist ${allowed.commit}`
+ );
+ }
+ if (allowed.tag && source.tag && source.tag !== allowed.tag) {
+ errors.push(
+ `flatpak git source ${source.url} tag ${source.tag} drifts from allowlist ${allowed.tag}`
+ );
+ }
+ if (!source.commit) {
+ errors.push(`flatpak git source ${source.url} is missing commit pin`);
+ }
+ }
+
+ for (const allowed of allowGitSources) {
+ if (!declaredGitSources.some((source) => source.url === allowed.url)) {
+ errors.push(
+ `allowlist git source ${allowed.url} is missing from package.json flatpak modules`
+ );
+ }
+ }
+
+ const wasmUrlTemplate = readExportedStringConst(fetchMicronText, "DEFAULT_WASM_URL");
+ const wasmExecUrl = readExportedStringConst(fetchMicronText, "DEFAULT_WASM_EXEC_URL");
+ // DEFAULT_WASM_URL is a template literal with ${MICRON_PARSER_GO_RELEASE_TAG}
+ const wasmUrlResolved = `https://github.com/Quad4-Software/Micron-Parser-Go/releases/download/${MICRON_PARSER_GO_RELEASE_TAG}/micron-parser-go.wasm`;
+
+ for (const entry of allowlist.download_urls || []) {
+ if (entry.name === "micron-parser-go-wasm") {
+ if (entry.pinned_tag && entry.pinned_tag !== MICRON_PARSER_GO_RELEASE_TAG) {
+ errors.push(
+ `Micron WASM tag ${MICRON_PARSER_GO_RELEASE_TAG} drifts from allowlist ${entry.pinned_tag}`
+ );
+ }
+ if (entry.pattern && !matchPattern(entry.pattern, wasmUrlResolved)) {
+ errors.push(
+ `Micron WASM URL ${wasmUrlResolved} does not match allowlist pattern ${entry.pattern}`
+ );
+ }
+ if (wasmUrlTemplate && !wasmUrlTemplate.includes("Micron-Parser-Go/releases/download/")) {
+ errors.push(`fetch-micron-wasm.mjs DEFAULT_WASM_URL looks unexpected: ${wasmUrlTemplate}`);
+ }
+ }
+ if (entry.name === "golang-wasm-exec") {
+ if (!wasmExecUrl) {
+ errors.push("fetch-micron-wasm.mjs DEFAULT_WASM_EXEC_URL missing");
+ } else if (entry.url && wasmExecUrl !== entry.url) {
+ errors.push(
+ `wasm_exec URL ${wasmExecUrl} drifts from allowlist ${entry.url}`
+ );
+ }
+ }
+ }
+
+ if (errors.length > 0) {
+ console.error("check-git-deps: failed");
+ for (const error of errors) {
+ console.error(` - ${error}`);
+ }
+ process.exit(1);
+ }
+
+ console.log("check-git-deps: ok");
+ console.log(` micron-parser commit: ${(allowByName.get("micron-parser") || {}).commit}`);
+ console.log(` micron wasm tag: ${MICRON_PARSER_GO_RELEASE_TAG}`);
+}
+
+main();

diff --git a/scripts/deps-allowlist.json b/scripts/deps-allowlist.json
new file mode 100644
index 00000000..b0328868
--- /dev/null
+++ b/scripts/deps-allowlist.json
@@ -0,0 +1,35 @@
+{
+ "version": 1,
+ "git_dependencies": [
+ {
+ "name": "micron-parser",
+ "specifiers": [
+ "github:RFnexus/micron-parser-js",
+ "github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098"
+ ],
+ "commit": "33feb1054c8b2cb3f5f05abbb8903360d3f0c098",
+ "repository": "https://github.com/RFnexus/micron-parser-js"
+ }
+ ],
+ "git_sources": [
+ {
+ "name": "zypak",
+ "url": "https://github.com/refi64/zypak",
+ "tag": "v2025.09",
+ "commit": "693a71c5ffa80ec9c9ce2ae03b1ccc493c698e53",
+ "declared_in": "package.json#build.flatpak.modules"
+ }
+ ],
+ "download_urls": [
+ {
+ "name": "micron-parser-go-wasm",
+ "pattern": "https://github.com/Quad4-Software/Micron-Parser-Go/releases/download/*/micron-parser-go.wasm",
+ "pinned_tag": "v1.0.5",
+ "tag_source": "scripts/micron-parser-go-version.mjs"
+ },
+ {
+ "name": "golang-wasm-exec",
+ "url": "https://raw.githubusercontent.com/golang/go/go1.26.2/lib/wasm/wasm_exec.js"
+ }
+ ]
+}

diff --git a/scripts/mutation/config.mjs b/scripts/mutation/config.mjs
index 1fbd0cfc..b26650f2 100644
--- a/scripts/mutation/config.mjs
+++ b/scripts/mutation/config.mjs
@@ -4,6 +4,12 @@
/** @typedef {{ source: string, tests: string[] }} MutationTarget */
+const LIBS_TESTS = [
+ "tests/frontend/libs.core.test.js",
+ "tests/frontend/libs.oracle.test.js",
+ "tests/frontend/libs.index.test.js",
+];
+
/** @type {MutationTarget[]} */
export const DEFAULT_FRONTEND_TARGETS = [
{
@@ -26,6 +32,22 @@ export const DEFAULT_FRONTEND_TARGETS = [
source: "meshchatx/src/frontend/js/reticulumPathfinding.js",
tests: ["tests/frontend/reticulumPathfinding.test.js"],
},
+ {
+ source: "meshchatx/src/frontend/libs/emitter.js",
+ tests: LIBS_TESTS,
+ },
+ {
+ source: "meshchatx/src/frontend/libs/uuid.js",
+ tests: LIBS_TESTS,
+ },
+ {
+ source: "meshchatx/src/frontend/libs/datetime.js",
+ tests: LIBS_TESTS,
+ },
+ {
+ source: "meshchatx/src/frontend/libs/clickOutside.js",
+ tests: LIBS_TESTS,
+ },
];
/**


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────